package com.zmm.util.document;

import com.zmm.util.document.collection.MapUtil;
import com.zmm.util.document.md.Md5Util;
import com.zmm.util.document.param.ParamHandle;
import com.zmm.util.document.register.CheckPermission;
import com.zmm.util.document.register.Param;
import com.zmm.util.document.wjl.IdoSelfGroup;
import com.zmm.util.document.wjl.IdoSelfItem;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternUtils;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.MethodMetadata;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Map;
import java.util.Set;

/**
 * @author 900045
 * @description:
 * @name AnnotationUtil
 * @date By 2021-10-26 16:45:19
 */
@Component
public class AnnotationUtil {
	

	@Autowired
	private ResourceLoader resourceLoader;

	private IdoSelfGroup group;
	
	private Map<String, IdoSelfItem> itemMap;

	private Map<String, Method> methodMap;
	
	/**
	 * 初始化 IdoSelfGroup  并保存 methodMap and itemMap
	 * @author: 900045
	 * @date: 2021-10-27 11:44:27
	 * @throws 
	 * @param classPath: 
	 * @param tagAnnotationClass: 
	 * @return: com.zmm.util.document.wjl.IdoSelfGroup
	 **/
	public IdoSelfGroup getAllAddTagAnnotationUrl(String classPath, Class tagAnnotationClass) throws Exception {
		
		// https://blog.csdn.net/MyNote1994/article/details/106228644/
		ResourcePatternResolver resolver = ResourcePatternUtils.getResourcePatternResolver(resourceLoader);
		MetadataReaderFactory metaReader = new CachingMetadataReaderFactory(resourceLoader);
		Resource[] resources = resolver.getResources(classPath);
		if (group != null) {
			return group;
		}
		methodMap = MapUtil.hashMap();
		itemMap = MapUtil.hashMap();
		group = new IdoSelfGroup();
		for (Resource r : resources) {
			MetadataReader reader = metaReader.getMetadataReader(r);
			resolveClass(reader, group, tagAnnotationClass);
		}
		return group;
	}

	/**
	 * 根据 code 查询 对应方法的参数以及返回值
	 * @author: 900045
	 * @date: 2021-10-27 11:45:08
	 * @throws 
	 * @param code: 
	 * @return: com.zmm.util.document.wjl.IdoSelfItem
	 **/
	public IdoSelfItem initParam(String code) throws Exception {
		if (itemMap == null) {
			getAllAddTagAnnotationUrl("classpath*:com/zmm/book/keep/service/**/*.class", CheckPermission.class);
		}
		IdoSelfItem item = itemMap.get(code);
		if (item == null) {
			return null;
		}
		Method method = methodMap.get(code);
		item.setReqParams(null);
		item.setRespParams(null);
		// 解析并封装方法的参数
		ParamHandle.parseReqParams(item, method.getParameters());
		// 解析并封装方法的返回值
		parseRespParams(item, method);
		return item;
	}

	

	/**
	 * 响应参数解析
	 * @author: 900045
	 * @date: 2021-10-27 11:49:56
	 * @throws 
	 * @param item: 
	 * @param method: 
	 * @return: void
	 **/
	private void parseRespParams(IdoSelfItem item, Method method) {
		CheckPermission methodAnnotation = method.getAnnotation(CheckPermission.class);
		if (methodAnnotation == null) {
			return;
		}
		if (methodAnnotation.respClass() == Object.class) {
			return;
		}
		Field[] fields = methodAnnotation.respClass().getDeclaredFields();
		IdoInfo idoInfo = new IdoInfo();
		idoInfo.setRetry(methodAnnotation.retry());
		idoInfo.setSuccess(methodAnnotation.success());
		idoInfo.setStatusCode(methodAnnotation.statusCode());
		idoInfo.setStatusMessage(methodAnnotation.statusMessage());
		item.setIdoInfo(idoInfo);
		ParamHandle.parseParamAnnotation(item, fields);
	}

	


	private IdoSelfGroup resolveClass(MetadataReader reader, IdoSelfGroup group, Class tagAnnotationClass) {
		String tagAnnotationClassCanonicalName = tagAnnotationClass.getCanonicalName();
		//获取注解元数据
		AnnotationMetadata annotationMetadata = reader.getAnnotationMetadata();
		String className = annotationMetadata.getClassName();
		
		Class<?> clazz = null ;
		try {
			clazz = Class.forName(className);
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
		if (ObjectUtils.isEmpty(clazz)) {
			return null;
		}
		Method[] methods = clazz.getDeclaredMethods();
		IdoSelfItem idoSelfItem;
		for (Method method : methods) {
			//所有方法上的注解
			Parameter[] parameters = method.getParameters();
			String key = className+method.getName();
			String code = Md5Util.sign(key, "", "UTF-8");
			for (Parameter parameter : parameters) {
				
				Param annotation = parameter.getAnnotation(Param.class);
				//获取指定注解

				if (annotation == null) {
					continue;
				}
				idoSelfItem = new IdoSelfItem();
				
				idoSelfItem.setMethodName(method.getName());
				idoSelfItem.setCode(code);
				idoSelfItem.setClassNamePath(className);
				idoSelfItem.setVersion(annotation.version());

				itemMap.put(code, idoSelfItem);
			}
			methodMap.put(code,method);
		}
		Set<MethodMetadata> annotatedMethods = annotationMetadata.getAnnotatedMethods(tagAnnotationClassCanonicalName);
		for (MethodMetadata annotatedMethod : annotatedMethods) {
			//获取当前方法中要扫描注解的属性
			Map<String, Object> targetAttr = annotatedMethod.getAnnotationAttributes(tagAnnotationClassCanonicalName);
			IdoSelfItem item = new IdoSelfItem();
			String methodName = annotatedMethod.getMethodName();
			String code = Md5Util.sign(className + methodName, "", "UTF-8");
			item.setCode(code);
			item.setVersion(targetAttr.get("version").toString());
			
			item.setMethodName(methodName);
			item.setClassNamePath(className);
			group.add(item);
		}
		return group;
	}

}
